#include "expresion.h"
#include "literal.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/*PRE: Recibe un string no vacio.
  POS: Analiza si el contenido del string corresponde a una funcion de 2 terminos o no. Devuelve 1 si es binaria o 0 si no lo es.*/
int Funcion_EsBinaria (const char* funcion){
  if (!strcmp(funcion,"sin")) return 0;
  if (!strcmp(funcion,"cos")) return 0;
  if (!strcmp(funcion,"ln")) return 0;
  return 1;
}

/*PRE: Recibe un puntero a un arbol creado y vacio, y un puntero a una pila creada.
  POS: Levanta los datos de la pila y los vuelca sobre el arbol. En caso de producirse algun error devuelve -1, sino devuelve 1*/
int Arbol_Expresion (TExpresion* expr, TPila* pila){
  int error;
  TLiteral literal;
  TAB Aux;
  char* el_literal;

  Aux=*expr;
  if (!P_Vacia(*pila)) P_Sacar(pila, &literal); else return -1;  //si la pila no esta vacia.


  if (AB_Vacio(*expr)){
    AB_Insertar(expr, RAIZ, &literal, &error);
    if (Literal_EsOperador(&literal))
      Arbol_Expresion (expr, pila);
  }else{
    AB_MoverCte(&Aux,DER,&error); //DER
    if (error){   //si no se puede mover.
      AB_Insertar(&Aux, DER, &literal, &error); //DER
      if (Literal_EsOperador(&literal))
        Arbol_Expresion (&Aux, pila);
    }
    Aux=*expr;
    AB_ElemCte(Aux,&literal);
    Literal_AString(&literal,&el_literal);
    if (Funcion_EsBinaria(el_literal)){
      AB_MoverCte(&Aux,IZQ,&error); //IZQ
      if (error){
        if (!P_Vacia(*pila)) P_Sacar(pila, &literal); else return -1;
        AB_Insertar(&Aux, IZQ, &literal, &error); //IZQ
        if (Literal_EsOperador(&literal)){
          Arbol_Expresion (&Aux, pila);
        }
      }
    }
    free(el_literal);
  }
  return 1;
}

/*PRE: Recibe un puntero a un arbol creado y no vacio, y un puntero a una pila creada.
  POS: Copia los datos del arbol en una pila.*/
void Polaca_Prefija(TExpresion* expr, TPila* pila){
  int error,num;
  TLiteral literal;
  char* el_literal,*aux;

  if (AB_Vacio(*expr)) return;
  AB_ElemCte(*expr,&literal);
  Literal_AString(&literal, &el_literal);

  num=atoi(el_literal);
  if (num<0){
    aux=(char*)malloc(sizeof(char)*(strlen(el_literal)+3));
    strcpy(aux,"(");
    strcat(aux,el_literal);
    strcat(aux,")");
    free(el_literal);
    el_literal=(char*)malloc(sizeof(char)*(strlen(aux)+1));
    strcpy(el_literal,aux);
    free(aux);
  }

  P_Agregar(pila,&el_literal);
  AB_MoverCte(expr,IZQ,&error);
  if (!error){
    Polaca_Prefija(expr,pila);
  }
  AB_MoverCte(expr,DER,&error);
  if (!error){
    Polaca_Prefija(expr,pila);
  }
  AB_MoverCte(expr,PAD,&error);
}

/*PRE: Recibe 3 strings y un puntero a otro string el cual no deve tener memoria allocada.
  POS: Concatena al operado1,operador y operando2 y agrega los parentesis en caso de ser necesario*/
void parentesis(char** linea, const char* operador, const char* operando1, const char* operando2 ){
  TPila pila;
  TLiteral literal;
  char* string;

  if(!strcmp(operador,"*")){
    P_Crear(&pila,sizeof(TLiteral));
    infijo_a_sufijo(operando1,"",&pila);
    P_Sacar(&pila,&literal);
    Literal_AString(&literal, &string);
    if( (!strcmp(string,"+")) || (!strcmp(string,"-")) || (!strcmp(string,"/")) || (!strcmp(string,"^")) ){
      *linea=(char*)malloc(sizeof(char)*(strlen(operando1)+3));
      strcpy(*linea,"(");
      strcat(*linea,operando1);
      strcat(*linea,")");
    }else{
      *linea=(char*)malloc(sizeof(char)*(strlen(operando1)+1));
      strcpy(*linea,operando1);
    }
    Literal_Destruir(&literal);
    free(string);
    if(!P_Vacia(pila)) P_Vaciar(&pila);
    *linea=(char*)realloc(*linea,sizeof(char*)*(strlen(*linea)+strlen(operador)+1));
    strcat(*linea,operador);
    P_Crear(&pila,sizeof(TLiteral));
    infijo_a_sufijo(operando2,"",&pila);
    P_Sacar(&pila,&literal);
    Literal_AString(&literal, &string);
    if( (!strcmp(string,"+")) || (!strcmp(string,"-")) || (!strcmp(string,"/")) ){
      *linea=(char*)realloc(*linea,sizeof(char)*(strlen(*linea)+strlen(operando2)+3));
      strcat(*linea,"(");
      strcat(*linea,operando2);
      strcat(*linea,")");
    }else{
      *linea=(char*)realloc(*linea,sizeof(char)*(strlen(*linea)+strlen(operando2)+1));
      strcat(*linea,operando2);
    }
    Literal_Destruir(&literal);
    free(string);
    if(!P_Vacia(pila)) P_Vaciar(&pila);
    return;
  }

  if(!strcmp(operador,"/")){
    P_Crear(&pila,sizeof(TLiteral));
    infijo_a_sufijo(operando1,"",&pila);
    P_Sacar(&pila,&literal);
    Literal_AString(&literal, &string);
    if( (!strcmp(string,"+")) || (!strcmp(string,"-")) || (!strcmp(string,"/")) || (!strcmp(string,"^")) || (!strcmp(string,"*")) ){
      *linea=(char*)malloc(sizeof(char)*(strlen(operando1)+3));
      strcpy(*linea,"(");
      strcat(*linea,operando1);
      strcat(*linea,")");
    }else{
      *linea=(char*)malloc(sizeof(char)*(strlen(operando1)+1));
      strcpy(*linea,operando1);
    }
    Literal_Destruir(&literal);
    free(string);
    if(!P_Vacia(pila)) P_Vaciar(&pila);
    *linea=(char*)realloc(*linea,sizeof(char*)*(strlen(*linea)+strlen(operador)+1));
    strcat(*linea,operador);
    P_Crear(&pila,sizeof(TLiteral));
    infijo_a_sufijo(operando2,"",&pila);
    P_Sacar(&pila,&literal);
    Literal_AString(&literal, &string);
    if( (!strcmp(string,"+")) || (!strcmp(string,"-")) || (!strcmp(string,"/")) || (!strcmp(string,"^")) || (!strcmp(string,"*")) ){
      *linea=(char*)realloc(*linea,sizeof(char)*(strlen(*linea)+strlen(operando2)+3));
      strcat(*linea,"(");
      strcat(*linea,operando2);
      strcat(*linea,")");
    }else{
      *linea=(char*)realloc(*linea,sizeof(char)*(strlen(*linea)+strlen(operando2)+1));
      strcat(*linea,operando2);
    }
    Literal_Destruir(&literal);
    free(string);
    if(!P_Vacia(pila)) P_Vaciar(&pila);
  return;
  }

  if(!strcmp(operador,"^")){
    P_Crear(&pila,sizeof(TLiteral));
    infijo_a_sufijo(operando1,"",&pila);
    P_Sacar(&pila,&literal);
    Literal_AString(&literal, &string);
    if( (!strcmp(string,"+")) || (!strcmp(string,"-")) || (!strcmp(string,"/")) || (!strcmp(string,"*")) ){
      *linea=(char*)malloc(sizeof(char)*(strlen(operando1)+3));
      strcpy(*linea,"(");
      strcat(*linea,operando1);
      strcat(*linea,")");
    }else{
      *linea=(char*)malloc(sizeof(char)*(strlen(operando1)+1));
      strcpy(*linea,operando1);
    }
    Literal_Destruir(&literal);
    free(string);
    if(!P_Vacia(pila)) P_Vaciar(&pila);
    *linea=(char*)realloc(*linea,sizeof(char*)*(strlen(*linea)+strlen(operador)+1));
    strcat(*linea,operador);
    P_Crear(&pila,sizeof(TLiteral));
    infijo_a_sufijo(operando2,"",&pila);
    P_Sacar(&pila,&literal);
    Literal_AString(&literal, &string);
    if( (!strcmp(string,"+")) || (!strcmp(string,"-")) || (!strcmp(string,"/")) || (!strcmp(string,"*")) ){
      *linea=(char*)realloc(*linea,sizeof(char)*(strlen(*linea)+strlen(operando2)+3));
      strcat(*linea,"(");
      strcat(*linea,operando2);
      strcat(*linea,")");
    }else{
      *linea=(char*)realloc(*linea,sizeof(char)*(strlen(*linea)+strlen(operando2)+1));
      strcat(*linea,operando2);
    }
    Literal_Destruir(&literal);
    free(string);
    if(!P_Vacia(pila)) P_Vaciar(&pila);
    return;
  }
  *linea=(char*)malloc(sizeof(char)*(strlen(operando1)+strlen(operador)+strlen(operando2)+1));
  strcpy(*linea,operando1);
  strcat(*linea,operador);
  strcat(*linea,operando2);


}

/*PRE: Recibe un puntero a pila la cual debe de estar creada y no vacia;
  POS: Resuelve los datos de la pila formando la exprecion y la devuelve en la misma pila como unico dato. En caso de producirse un error devuelve 0 sino devuelve 1*/
int resolver (TPila* pila){
  TPila AUX,AUX2;
  TLiteral literal;
  char *linea,*linea2,*Operando1,*Operando2,*Operador;
  int corte=0;
  P_Crear(&AUX,sizeof(char*));
  P_Crear(&AUX2,sizeof(TLiteral));

  if (!P_Vacia(*pila)) {
    corte=0;
    do{
    P_Sacar (pila,&linea);
    infijo_a_sufijo(linea, "", &AUX2);
    P_Sacar (&AUX2,&literal);
    Literal_AString(&literal,&linea2);
    if (!strcmp(linea,linea2)){
      if (P_Vacia(AUX2)){      //si tenia un solo elemento.
        if (Literal_EsOperador(&literal))
          corte=1;
      }else{
      }
    }else{
      Literal_Destruir(&literal);
      P_Vaciar(&AUX2);
    }
    free(linea2);
    if ((P_Vacia(*pila))&&(!corte)){
      P_Agregar(pila,&linea);
      corte=2;
    }else{
      P_Agregar(&AUX,&linea);
    }
    while(!P_Vacia(AUX2)){
      P_Sacar(&AUX2,&literal);
      Literal_Destruir(&literal);
      free(linea2);
    }

    }while (!corte);

    if (corte==2) return 0;    //Pila vacia

    if (Funcion_EsBinaria(linea)){
      P_Sacar(&AUX,&linea);
      Operador=(char*)malloc(sizeof(char)*(strlen(linea)+1));
      strcpy(Operador,linea);
      free(linea);
      P_Sacar(&AUX,&linea);
      Operando1=(char*)malloc(sizeof(char)*(strlen(linea)+1));
      strcpy(Operando1,linea);
      free(linea);
      P_Sacar(&AUX,&linea);
      Operando2=(char*)malloc(sizeof(char)*(strlen(linea)+1));
      strcpy(Operando2,linea);
      free(linea);
      parentesis(&linea, Operador, Operando1, Operando2 );
      free(Operador);
      free(Operando1);
      free(Operando2);
      P_Agregar(&AUX,&linea);
    }else{
      P_Sacar(&AUX,&linea);
      Operador=(char*)malloc(sizeof(char)*(strlen(linea)+1));
      strcpy(Operador,linea);
      free(linea);
      P_Sacar(&AUX,&linea);
      Operando1=(char*)malloc(sizeof(char)*(strlen(linea)+1));
      strcpy(Operando1,linea);
      free(linea);
      linea=(char*)malloc(sizeof(char)*(strlen(Operador)+strlen(Operando1)+3));   // '\0'  ;  '('  ;  ')'
      strcpy(linea,Operador);
      strcat(linea,"(");
      strcat(linea,Operando1);
      strcat(linea,")");
      free(Operador);
      free(Operando1);
      P_Agregar(&AUX,&linea);
    }
    while (!P_Vacia(AUX)){
      P_Sacar(&AUX,&linea);
      P_Agregar(pila,&linea);
    }
  }else{
    return 0;
  }
  return 1;
}


/*PRE: Recive un puntero a un arbol de expresion no creado.
  POS: Crea el arbol*/
int Expresion_Crear (TExpresion* expr){
  AB_Crear(expr,sizeof(TLiteral));
  return 1;
}

/*PRE: Recibe un puntero a un arbol de expresion creado, y dos strings.
       string: se espera la formula matematica
       var: indica cual es la variable independiente.
  POS: Genera el arbol de expresion correspondiende a la expresion de string. Si se produce algun error devuelve -1 sino devuelve 1*/
int Expresion_Parsear (TExpresion* expr, const char* string, const char* var){
  int error;
  TPila pila;
  P_Crear(&pila, sizeof(TLiteral));
  if (string==NULL) return -1;
  error=infijo_a_sufijo(string, var, &pila);
  if (!error) return -1;
  Arbol_Expresion (expr, &pila);
  AB_MoverCte(expr,RAIZ,&error);
  return 0;
}

/*PRE: Recibe un puntero a un arbol de expresion creado y no vacio, y otro puntero a un arbol no creado.
  POS: Crea el arbol de salida y devuelve el arbol de expresion derivado. Devuelve 1 si logro derivar o 0 si ocurre algun error*/
int Expresion_Derivar (const TExpresion* expr, TExpresion* deriv){
  int error;
  TLiteral literal;

  AB_MoverCte(expr,RAIZ,&error);
  if (error) return -1;
  AB_Crear(deriv,sizeof(TLiteral));
  AB_ElemCte(*expr,&literal);

  if(!Literal_Derivar(&literal, expr, deriv)){
    if (!AB_Vacio(*deriv)) AB_Vaciar(deriv);
    return 0;
  }

  AB_MoverCte(deriv,RAIZ,&error);
  return 1;
}

/*PRE: Recibe un puntero a un arbol de expresion creado y no vacio, y otro puntero a un arbol no creado.
  POS: Crea el arbol de salida y devuelve el arbol de expresion simplificado. Retorna 1 si pudo simplificar la expresion o retorna el error devuelto por las primitivas de simplificacion*/
int Expresion_Simplificar (const TExpresion* expr, TExpresion* simpl){
  int error;
  TLiteral literal;
  TExpresion AUX;
  char *linea1,*linea2;

  AB_MoverCte(expr,RAIZ,&error);
  if (error) return -1;
  AB_Crear(simpl,sizeof(TLiteral));
  AB_Crear(&AUX,sizeof(TLiteral));
  AB_ElemCte(*expr,&literal);


  error=Literal_Simplificar(&literal, expr, &AUX);
  if(error!=1){
    if (!AB_Vacio(AUX)) AB_Vaciar(&AUX);
    return error;
  }
  AB_MoverCte(&AUX,RAIZ,&error);


  Expresion_AString(expr,&linea1);
  Expresion_AString(&AUX,&linea2);
  AB_MoverCte(&AUX,RAIZ,&error);
  if(strcmp(linea1,linea2)){
    Expresion_Simplificar(&AUX,simpl);
  }else{
    Expresion_Copiar_SubExpresion(&AUX,simpl,RAIZ);
  }
    AB_Vaciar(&AUX);
  free(linea1);
  free(linea2);

  return 1;

}

/*PRE: Recibe el puntero a un arbol de expresion creado y no vacio, y un puntero a un string sin memoria allocada.
  POS: Recorre el arbolde expreion y devuelve la funcion que representa en el string. En caso de producirse algun error devuelve -1 sino devuelve 1*/
int Expresion_AString (const TExpresion* expr, char** string){
  TPila pila;

  if (AB_Vacio(*expr)) return -1;
  P_Crear(&pila,sizeof(char*));
  Polaca_Prefija(expr, &pila);
  while (resolver(&pila));
  if(!P_Vacia(pila)){
    P_Sacar(&pila,string);
  }else{
    *string=NULL;
  }
  return 1;
}

/*PRE: Recibe el puntero de un arbol de expresion creado y no vacio.
  POS: Libera la memoria del arbol de expresion. devuelve 1 si pudo liberar correctamente la memoria, o 0 si tubo algun error*/
int Expresion_Destruir (TExpresion* expr){
  AB_Vaciar(expr);
  return 1;
}

/*  FUNCION AUXILIAR
PRE: Recivbe los punteros de arbol de expresion origen creado y no vacio y otro destino no creado, y un tipo de movimiento
     movim aceptta como movimientos: RAIZ, DER e IZQ.
POS: Crea el arbol destino y copia las subrramas del origen a partir del movimiento indicado respecto del corriente del origen*/
void Expresion_Copiar_SubExpresion (const TExpresion* origen, TExpresion* destino, int movim){
  int error;

  AB_Crear(destino,sizeof(TLiteral));
	AB_CopiarSubarbol(origen,destino,movim);
  AB_MoverCte(destino,RAIZ,&error);
}

